package lark

import (
	"context"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"time"

	"gitee.com/AlexWoo/arpc"
	"github.com/zeromicro/go-zero/rest/httpc"
)

var (
	formatString = "2006-01-02 15:04:05 -0700 MST"
	defaultLark  *Lark
)

type Text struct {
	Tag  string `json:"tag"`
	Text string `json:"text"`
}

func newText(text string) *Text {
	return &Text{
		Tag:  "text",
		Text: text,
	}
}

type Href struct {
	Tag  string `json:"tag"`
	Text string `json:"text"`
	Href string `json:"href"`
}

// func newHref(text string, href string) *Href {
// 	return &Href{
// 		Tag:  "a",
// 		Text: text,
// 		Href: href,
// 	}
// }

type At struct {
	Tag    string `json:"tag"`
	UserID string `json:"user_id"`
}

func newAt(uid string) *At {
	return &At{
		Tag:    "at",
		UserID: uid,
	}
}

type ParaItem []interface{}

type Content struct {
	Title   string   `json:"title"`
	Content ParaItem `json:"content"`
}

type ZhCn struct {
	ZhCn Content `json:"zh_cn"`
}

type LarkContent struct {
	Post ZhCn `json:"post"`
}

type LarkSign struct {
	Timestamp string `json:"timestamp"`
	Sign      string `json:"sign"`
}

type LarkMessage struct {
	*LarkSign
	MsgType string      `json:"msg_type"`
	Content LarkContent `json:"content"`
}

type LarkResponse struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
}

type LarkPack interface {
	GenLarkTitle(*Content)
	GenLarkLabel(*Content)
	GenLarkContent(*Content)
	GenLarkReminder(*Content)
}

func genLarkSign(secret string, timestamp int64) (string, error) {
	//timestamp + key 做sha256, 再进行base64 encode
	stringToSign := fmt.Sprintf("%v", timestamp) + "\n" + secret

	var data []byte
	h := hmac.New(sha256.New, []byte(stringToSign))

	_, err := h.Write(data)
	if err != nil {
		return "", err
	}

	signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
	return signature, nil
}

func genLarkMessage(lp LarkPack, secret string) (*LarkMessage, error) {
	var ls *LarkSign = nil

	// Generate Signature
	if secret != "" {
		timestamp := time.Now().Unix()
		sign, err := genLarkSign(secret, timestamp)
		if err != nil {
			return nil, err
		}

		ls = &LarkSign{
			Timestamp: fmt.Sprintf("%d", timestamp),
			Sign:      sign,
		}
	}

	// Generate Content
	content := &Content{
		Content: ParaItem{},
	}
	lp.GenLarkTitle(content)
	lp.GenLarkLabel(content)
	lp.GenLarkContent(content)
	lp.GenLarkReminder(content)

	// Lark Message
	lm := &LarkMessage{
		LarkSign: ls,
		MsgType:  "post",
		Content: LarkContent{
			Post: ZhCn{
				ZhCn: *content,
			},
		},
	}

	return lm, nil
}

type Lark struct {
	service  string
	cluster  string
	instance string

	conf *LarkConf
}

func (l *Lark) sendLarkAlert(la LarkPack) (int, error) {
	msg, err := genLarkMessage(la, l.conf.LarkBotSecret)
	if err != nil {
		return -1, fmt.Errorf("GenAlertLarkMessage error: %v", err)
	}

	resp, err := httpc.Do(context.Background(), http.MethodPost, l.conf.LarkBotURL, msg)
	if err != nil {
		return -1, fmt.Errorf("send larkAlert error: %v", err)
	}

	b, err := io.ReadAll(resp.Body)
	if err != nil {
		return -1, fmt.Errorf("get larkAlert body error: %d %v", resp.StatusCode, err)
	}

	lr := &LarkResponse{}
	err = json.Unmarshal(b, lr)
	if err != nil {
		return -1, fmt.Errorf("unmarshal response body error: %d %v", resp.StatusCode, err)
	}

	if lr.Code != 0 {
		return lr.Code, fmt.Errorf("lark return error: %d %v", resp.StatusCode, lr.Msg)
	}

	return lr.Code, nil
}

func MustInitLark(service string, cluster string, instance string, conf *LarkConf) {
	if defaultLark == nil {
		conf.ReplaceEnv()

		if conf.LarkBotURL == "" {
			log.Fatal("error: init lark error, LarkBotURL unset")
			return
		}

		defaultLark = &Lark{
			service:  service,
			cluster:  cluster,
			instance: instance,
			conf:     conf,
		}
	}
}

func AlertLow(item string, begin time.Time, end time.Time, content string, args ...any) {
	if defaultLark == nil {
		return
	}

	c := fmt.Sprintf(content, args...)

	la := defaultLark.NewLarkAlert(item, "Low", begin, end, nil, c)
	if code, err := defaultLark.sendLarkAlert(la); err != nil {
		arpc.Errorf("Send Lark Alert low failed, %d, %s", code, err.Error())
	}
}

func AlertMiddle(item string, begin time.Time, end time.Time, at []string, content string, args ...any) {
	if defaultLark == nil {
		return
	}

	c := fmt.Sprintf(content, args...)

	la := defaultLark.NewLarkAlert(item, "Middle", begin, end, at, c)
	if code, err := defaultLark.sendLarkAlert(la); err != nil {
		arpc.Errorf("Send Lark Alert middle failed, %d, %s", code, err.Error())
	}
}

func AlertHigh(item string, begin time.Time, end time.Time, at []string, content string, args ...any) {
	if defaultLark == nil {
		return
	}

	c := fmt.Sprintf(content, args...)

	la := defaultLark.NewLarkAlert(item, "High", begin, end, at, c)
	if code, err := defaultLark.sendLarkAlert(la); err != nil {
		arpc.Errorf("Send Lark Alert high failed, %d, %s", code, err.Error())
	}
}

func Notify(item string, at []string, content string, args ...any) {
	if defaultLark == nil {
		return
	}

	c := fmt.Sprintf(content, args...)

	ln := defaultLark.NewLarkNotification(item, at, c)
	if code, err := defaultLark.sendLarkAlert(ln); err != nil {
		arpc.Errorf("Send Lark Notification failed, %d, %s", code, err.Error())
	}
}
